# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2013-2014,2016
# Author: Lucas Meneghel Rodrigues <lmr@redhat.com>
# Author: Lukas Doktor <ldoktor@redhat.com>

import json
import sys

from avocado.core import exit_codes
from avocado.core.output import LOG_UI
from avocado.core.plugin_interfaces import CLICmd
from avocado.core.settings import settings
from avocado.core.varianter import Varianter

_VERBOSITY_LEVELS = {
    "none": 0,
    "brief": 1,
    "normal": 2,
    "verbose": 3,
    "full": 4,
    "max": 99,
}


def map_verbosity_level(level):
    if level.isdigit():
        return int(level)
    level = level.lower()
    if level in _VERBOSITY_LEVELS:
        return _VERBOSITY_LEVELS[level]
    else:
        raise ValueError


class Variants(CLICmd):

    """
    Implements "variants" command to visualize/debug test variants and params
    """

    name = "variants"
    description = "Tool to analyze and visualize test variants and params"

    def configure(self, parser):
        parser = super().configure(parser)
        lvl = ", ".join(sorted(_VERBOSITY_LEVELS, key=lambda _: _VERBOSITY_LEVELS[_]))
        verbosity_levels = f"(positive integer - 0, 1, ... - or {lvl}"

        help_msg = "Verbosity of the variants summary. " + verbosity_levels
        settings.register_option(
            section="variants",
            key="summary",
            key_type=map_verbosity_level,
            default=0,
            help_msg=help_msg,
            parser=parser,
            long_arg="--summary",
            metavar="SUMMARY",
        )

        help_msg = "Verbosity of the list of variants. " + verbosity_levels
        settings.register_option(
            section="variants",
            key="variants",
            key_type=map_verbosity_level,
            default=1,
            help_msg=help_msg,
            parser=parser,
            long_arg="--variants",
            metavar="VARIANTS",
        )

        help_msg = "[obsoleted by --variants] Shows the node content (variables)"
        settings.register_option(
            section="variants",
            key="contents",
            key_type=bool,
            default=False,
            parser=parser,
            help_msg=help_msg,
            short_arg="-c",
            long_arg="--contents",
        )

        help_msg = "Dump the Variants to a JSON serialized file"
        settings.register_option(
            section="variants",
            key="json_variants_dump",
            help_msg=help_msg,
            default=None,
            parser=parser,
            long_arg="--json-variants-dump",
            metavar="FILE",
        )

        env_parser = parser.add_argument_group("environment view options")

        help_msg = "Use debug implementation to gather more information."
        settings.register_option(
            section="variants",
            key="debug",
            help_msg=help_msg,
            parser=env_parser,
            key_type=bool,
            default=False,
            long_arg="--debug",
            short_arg="-d",
        )

        tree_parser = parser.add_argument_group("tree view options")

        help_msg = "[obsoleted by --summary] Shows the multiplex tree structure"
        settings.register_option(
            section="variants",
            key="tree",
            key_type=bool,
            default=False,
            help_msg=help_msg,
            parser=tree_parser,
            long_arg="--tree",
            short_arg="-t",
        )

        help_msg = "[obsoleted by --summary] Show the inherited values"
        settings.register_option(
            section="variants",
            key="inherit",
            key_type=bool,
            help_msg=help_msg,
            parser=tree_parser,
            default=False,
            short_arg="-i",
            long_arg="--inherit",
        )

    def run(self, config):
        tree = config.get("variants.tree")
        summary = config.get("variants.summary")
        variants = config.get("variants.variants")
        contents = config.get("variants.contents")
        inherit = config.get("variants.inherit")

        err = None
        if tree and config.get("variants.debug"):
            err = "Option --tree is incompatible with --debug."
        elif not tree and inherit:
            err = "Option --inherit can be only used with --tree"
        if err:
            LOG_UI.error(err)
            sys.exit(exit_codes.AVOCADO_FAIL)
        varianter = Varianter()
        try:
            varianter.parse(config)
        except (IOError, ValueError) as details:
            LOG_UI.error("Unable to parse varianter: %s", details)
            sys.exit(exit_codes.AVOCADO_FAIL)
        # Parse obsolete options (unsafe to combine them with new args)
        if tree:
            variants = 0
            summary += 1
            if contents:
                summary += 1
            if inherit:
                summary += 2
        else:
            if contents:
                variants += 2

        json_variants_dump = config.get("variants.json_variants_dump")
        # Export the serialized variants
        if json_variants_dump is not None:
            try:
                with open(json_variants_dump, "w", encoding="utf-8") as variants_file:
                    json.dump(varianter.dump(), variants_file)
            except IOError:
                LOG_UI.error("Cannot write %s", json_variants_dump)
                sys.exit(exit_codes.AVOCADO_FAIL)

        # Produce the output
        lines = varianter.to_str(
            summary=summary,
            variants=variants,
            use_utf8=sys.getdefaultencoding() == "utf-8",
        )
        for line in lines.splitlines():
            LOG_UI.debug(line)

        sys.exit(exit_codes.AVOCADO_ALL_OK)
